﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Windows;
using P4 = Red.Core.Perforce;

namespace Red.Launcher.ViewModels.Workspaces.Database
{
    public class P4ClientSpecification : Record, ICloneable
    {
        #region Public Static consts

        public static readonly string INVALIDCHARS_REGEX = @"[`¬!""£\$%\^&\*\(\)\{\}\[\]\\\/\|:\?,#\+;<>'=@ ]";

        #endregion

        #region Private Data

        // This data comes from the specification record itself
        private string _clientName;
        private string _stream;
        private string _streamFilter;
        private bool _optionAllWrite;
        private bool _optionClobber;
        private bool _optionCompress;
        private bool _optionModTime;
        private bool _optionRMDir;
        private P4.LineEndings _optionLineEndings;
        private P4.SubmitType _optionOnSubmit;

        // This data comes from a separate table
        private bool _disabled;

        // Which tables should be updated
        private bool _specifcationModified;
        private bool _disabledModified;

        #endregion Private Data

        #region Public Properties

        public static Metadata METADATA { get; private set; }

        public string ClientName
        {
            get
            {
                if ( !string.IsNullOrEmpty( _clientName ) )
                {
                    return _clientName;
                }

                if ( !string.IsNullOrEmpty( Name ) )
                {
                    return Regex.Replace( Name.ToLowerInvariant(), INVALIDCHARS_REGEX, @"_" );
                }

                return null;
            }

            set
            {
                if ( _clientName != value )
                {
                    _clientName = value;
                    OnPropertyChanged();
                }
            }
        }

        public string Stream
        {
            get { return _stream; }
            set
            {
                if ( _stream != value )
                {
                    _stream = value;
                    OnPropertyChanged();
                }
            }
        }

        public string StreamFilter
        {
            get { return string.IsNullOrWhiteSpace( _streamFilter )? null : _streamFilter; }
            set
            {
                if ( _streamFilter != value )
                {
                    _streamFilter = value;
                    OnPropertyChanged();
                }
            }
        }

        public bool OptionAllWrite
        {
            get { return _optionAllWrite; }
            set
            {
                if ( _optionAllWrite != value )
                {
                    _optionAllWrite = value;
                    OnPropertyChanged();
                }
            }
        }

        public bool OptionClobber
        {
            get { return _optionClobber; }
            set
            {
                if ( _optionClobber != value )
                {
                    _optionClobber = value;
                    OnPropertyChanged();
                }
            }
        }

        public bool OptionCompress
        {
            get { return _optionCompress; }
            set
            {
                if ( _optionCompress != value )
                {
                    _optionCompress = value;
                    OnPropertyChanged();
                }
            }
        }

        public bool OptionModTime
        {
            get { return _optionModTime; }
            set
            {
                if ( _optionModTime != value )
                {
                    _optionModTime = value;
                    OnPropertyChanged();
                }
            }
        }

        public bool OptionRMDir
        {
            get { return _optionRMDir; }
            set
            {
                if ( _optionRMDir != value )
                {
                    _optionRMDir = value;
                    OnPropertyChanged();
                }
            }
        }

        public bool Disabled
        {
            get { return _disabled; }
            set
            {
                if( _disabled != value )
                {
                    _disabled = value;
                    OnPropertyChanged();
                }
            }
        }

        public P4.LineEndings OptionLineEndings
        {
            get { return _optionLineEndings; }
            set
            {
                if ( _optionLineEndings != value )
                {
                    _optionLineEndings = value;
                    OnPropertyChanged();
                }
            }
        }

        public P4.SubmitType OptionOnSubmit
        {
            get { return _optionOnSubmit; }
            set
            {
                if ( _optionOnSubmit != value )
                {
                    _optionOnSubmit = value;
                    OnPropertyChanged();
                }
            }
        }

        #endregion Public Properties

        #region Implemented Properties

        public override Metadata Metadata { get { return METADATA; } }
        public override string Icon { get { return "appbar_lamp_desk_variant"; } }

        public override IEnumerable<object> Values
        {
            get
            {
                return new List<string>()
                {
                    Name,
                    ClientName,
                    Stream,
                    StreamFilter,
                    OptionAllWrite.ToString(),
                    OptionClobber.ToString(),
                    OptionCompress.ToString(),
                    OptionModTime.ToString(),
                    OptionRMDir.ToString(),
                    OptionLineEndings.ToString(),
                    OptionOnSubmit.ToString()
                };
            }
        }

        #endregion Implemented Properties

        #region Event Handlers

        private void P4ClientSpecification_PropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            if ( e.PropertyName == "Name" )
            {
                if ( string.IsNullOrEmpty( _clientName ) )
                {
                    OnPropertyChanged( "ClientName" );
                }
            }

            if( IsWatchingForChanges )
            {
                if ( e.PropertyName == "Disabled" )
                {
                    _disabledModified = true;
                }
                else
                {
                    _specifcationModified = true;
                }
            }
        }

        #endregion Event Handlers

        #region Overridden Methods

        #region Sql

        private static Sql.Select GenerateSelectSql( Record parent )
        {
            Sql.Select.Table source = Sql.Select.CreateTable( P4ClientSpecification.METADATA.Table, "a" );
            Sql.Select query = new Sql.Select();
            query.Sources.Add( source );
            query.AddColumn( "*", source );

            Sql.Select.Table innerSource = Sql.Select.CreateTable( P4ClientSpecification.METADATA.Table );
            Sql.Select inner = new Sql.Select();
            Sql.Select.Column innerId = inner.AddColumn( "id" );
            inner.Sources.Add( innerSource );
            inner.AddColumn( "revision", null, "revision", "MAX" );
            inner.GroupBy.Add( innerId );

            Sql.Select.Join join = Sql.Select.CreateJoin( inner, Sql.Select.JoinType.Inner, "b" );
            join.Conditions.AddVar( "id", "id", source, join );
            join.Conditions.AddVar( "revision", "revision", source, join );

            query.Sources.Add( join );

            Sql.Select.Table disabledSource = Sql.Select.CreateTable( DisabledP4ClientSpecification.METADATA.Table );
            Sql.Select.Join disabledJoin = Sql.Select.CreateJoin( disabledSource, Sql.Select.JoinType.Left, "d" );
            disabledJoin.Conditions.AddVar( "id", P4ClientSpecification.METADATA.ForeignKey, source, disabledJoin );

            Sql.Select mostRecentQuery = new Sql.Select();
            mostRecentQuery.AddColumn( "created_at", disabledSource, "created_at", "MAX" );
            mostRecentQuery.Conditions.AddVar( "id", P4ClientSpecification.METADATA.ForeignKey, source, mostRecentQuery );
            mostRecentQuery.Sources.Add( disabledSource );
            disabledJoin.Conditions.AddSubQuery( "created_at", mostRecentQuery, disabledJoin );

            query.Sources.Add( disabledJoin );
            query.AddColumn( "id", disabledJoin, "dcs_id" );
            query.AddColumn( "disabled", disabledJoin );

            return query;
        }

        protected override void GenerateUpdateSql( ref Sql.Transaction transaction, Record parent )
        {
            if( _specifcationModified )
            {
                Sql.Insert query = new Sql.Insert( this );

                transaction.Add( query );
            }

            if( _disabledModified )
            {
                DisabledP4ClientSpecification disabled = new DisabledP4ClientSpecification()
                {
                    Disabled = Disabled
                };

                Sql.Insert query = new Sql.Insert( disabled );
                query.Parents.Add( this );

                transaction.Add( query );
            }
        }

        #endregion Sql

        #region Drag and drop

        internal override bool AcceptsDraggedData( IDataObject data )
        {
            return data.GetDataPresent( typeof( AssignedUserGroup ) ) || data.GetDataPresent( typeof( User ) ) || data.GetDataPresent( typeof( ClientParameter ) );
        }

        private bool PerformDrop<TData, TChild>( IDataObject data )
            where TChild : Record
            where TData : Record
        {
            DraggedRecord access = data.GetData( typeof( TData ) ) as DraggedRecord;

            if ( access != null )
            {
                Record child = (Record)Activator.CreateInstance( typeof( TChild ), new object[] { (TData)access.Record } );

                AddChild( ref child );

                return true;
            }

            return false;
        }

        internal override void PerformDrop( IDataObject data )
        {
            if ( PerformDrop<AssignedUserGroup, GroupToClientSpecs>( data ) )
            {
                return;
            }
            else if ( PerformDrop<User, UserToClientSpecs>( data ) )
            {
                return;
            }

            PerformDrop<ClientParameter, ClientParameter>( data );
        }

        #endregion Drag and drop

        #endregion Overridden Methods

        #region C-Tor

        static P4ClientSpecification()
        {
            METADATA = new Metadata()
            {
                Table = "P4ClientSpecs2",
                ForeignKey = "p4specs_id",
                Keys = new List<string>()
                {
                    "display_name",
                    "client_name",
                    "stream",
                    "stream_filter",
                    "opt_allwrite",
                    "opt_clobber",
                    "opt_compress",
                    "opt_modtime",
                    "opt_rmdir",
                    "opt_lineendings",
                    "opt_submit"
                },
                CreateDefault = () => { return new P4ClientSpecification(); },
                CreateFromDatabaseRow = ( Dictionary<string, object> data ) =>
                {
                    object disabled;
                    if( !data.TryGetValue( "disabled", out disabled ) )
                    {
                        disabled = DBNull.Value;
                    }

                    object streamFilter;
                    if ( !data.TryGetValue( "stream_filter", out streamFilter ) )
                        streamFilter = DBNull.Value;

                    return new P4ClientSpecification()
                    {
                        Id = (int)data[ "id" ],

                        Name = (string)data[ "display_name" ],
                        ClientName = (string)data[ "client_name" ],
                        Stream = (string)data[ "stream" ],
                        StreamFilter = streamFilter.Equals( DBNull.Value ) ? null : (string)streamFilter,

                        OptionAllWrite = (bool)data[ "opt_allwrite" ],
                        OptionClobber = (bool)data[ "opt_clobber" ],
                        OptionCompress = (bool)data[ "opt_compress" ],
                        OptionModTime = (bool)data[ "opt_modtime" ],
                        OptionRMDir = (bool)data[ "opt_rmdir" ],

                        Disabled = disabled.Equals( DBNull.Value ) ? false : (bool)disabled,

                        OptionLineEndings = (P4.LineEndings)Enum.Parse( typeof( P4.LineEndings ), (string)data[ "opt_lineendings" ] ),
                        OptionOnSubmit = (P4.SubmitType)Enum.Parse( typeof( P4.SubmitType ), (string)data[ "opt_submit" ] )
                    };
                },
                GenerateSelectSql = GenerateSelectSql
            };

            METADATA.Children.Add( typeof( GroupToClientSpecs ) );
            METADATA.Children.Add( typeof( ADGroupToClientSpecs ) );
            METADATA.Children.Add( typeof( UserToClientSpecs ) );
            METADATA.Children.Add( typeof( ClientParameter ) );
            METADATA.Joins.Add( typeof( DisabledP4ClientSpecification ) );
        }

        public P4ClientSpecification()
        {
            PropertyChanged += P4ClientSpecification_PropertyChanged;
        }

        #endregion C-Tor

        #region ICloneable

        public object Clone()
        {
            return MemberwiseClone();
        }

        public void Copy( P4ClientSpecification other)
        {
            Name                = other.Name;
            ClientName          = other.ClientName;
            Stream              = other.Stream;
            StreamFilter        = other.StreamFilter;

            OptionAllWrite      = other.OptionAllWrite;
            OptionClobber       = other.OptionClobber;
            OptionCompress      = other.OptionCompress;
            OptionModTime       = other.OptionModTime;
            OptionRMDir         = other.OptionRMDir;

            OptionLineEndings   = other.OptionLineEndings;
            OptionOnSubmit      = other.OptionOnSubmit;

            Disabled            = other.Disabled;

            OnPropertyChanged( "Stream" );
        }

        #endregion ICloneable
    }
}
